#!/usr/bin/env python

import sys
import time
import ConfigParser
import thread

try:
    import Pyro.core
except ImportError:
    print "Missing Pyro module, you will need to install Pyro"
    print "before running astmonpy, check http://pyro.sourceforge.net"
    sys.exit(1)

try:
    from wx import *
    import wx.lib.sheet as sheet
except ImportError:
    print "Missing wxPython module, you will need to install wxPython"
    print "before running astmonpy, check http://www.wxpython.org"
    sys.exit(1)

GREENTH = "#00CC33"
YELLOWTH = "#CCFF00"
REDTH = "#990000"

class AstmonMainFrame(Frame):
    def __init__(self, numclients, clients):
        Frame.__init__(self, None, ID_ANY, "Astmon Client", 
                        style=SYSTEM_MENU | CAPTION | CLOSE_BOX)

        # timer
        self.t1 = Timer(self)
        self.t1.Start(3000)
        self.Bind(EVT_TIMER, updateStatus)

        # Initialize the spreadsheet
        self.spreadsheet = sheet.CSheet(self)
        self.spreadsheet.EnableEditing(False)
        self.spreadsheet.EnableDragRowSize(False)
        self.spreadsheet.EnableDragGridSize(False)
        self.spreadsheet.EnableDragColSize(False)
        self.spreadsheet.EnableCellEditControl(False)
        self.spreadsheet.SetNumberRows(int(numclients))
        self.spreadsheet.SetNumberCols(10)

        # set spreadsheet client names 
        max = 0
        for count in range(int(numclients)):
            self.spreadsheet.SetRowLabelValue(count, clients[count]["name"])
            if len(clients[count]["name"]) > max: 
                max = len(clients[count]["name"])
        self.spreadsheet.SetRowLabelSize(max * 9)

        # set spreadsheet data names
        self.spreadsheet.SetColSize(0, 100)
        self.spreadsheet.SetColSize(3, 50)
        self.spreadsheet.SetColSize(4, 90)
        self.spreadsheet.SetColSize(5, 50)
        self.spreadsheet.SetColSize(7, 90)
        self.spreadsheet.SetColSize(8, 100)
        self.spreadsheet.SetColLabelValue(0, "Disk Usage")
        self.spreadsheet.SetColLabelValue(1, "Load Avg")
        self.spreadsheet.SetColLabelValue(2, "Memory")
        self.spreadsheet.SetColLabelValue(3, "Swap")
        self.spreadsheet.SetColLabelValue(4, "Sys Uptime")
        self.spreadsheet.SetColLabelValue(5, "CPU")
        self.spreadsheet.SetColLabelValue(6, "Process")
        self.spreadsheet.SetColLabelValue(7, "Ast Uptime")
        self.spreadsheet.SetColLabelValue(8, "Ast Channels")
        self.spreadsheet.SetColLabelValue(9, "Ast Calls")

        self.spreadsheet.SetInitialSize()

        gs = GridBagSizer(0, 0)
        gs.Add(self.spreadsheet, (0,0))
        self.SetSizer(gs)

        # status bar
        self.sb = self.CreateStatusBar()
        self.Fit()          
        self.Centre()
        self.Show(True)


def updateStatus(event):
    # start getting remote data
    count = 0
    for stat in stats:
        try:
            # DISK USAGE
            disks = stat["class"].getDiskUsage()
            max = 0
            for disk in disks:
                disk_usage_percent = 100 -((disk["free_blocks"] * 100) 
                                        / disk["total_blocks"])
                disk_usage_percent_name = disk["mnt_point"]
                if disk_usage_percent > max: 
                    max = disk_usage_percent
                    max_name = disk_usage_percent_name
            astmonframe.spreadsheet.SetCellValue(count, 0, str(max) + 
                                                "%% (%s)" % max_name)
            # Check disk usage Threshold
            setCellThreshold(count, 0, stat["diskusageth"], max)

            # LOAD AVERAGE
            loadavg = stat["class"].getLoadStats()["min1"]
            astmonframe.spreadsheet.SetCellValue(count, 1, str(loadavg))
            # Check load average threshold
            setCellThreshold(count, 1, stat["loadavgth"], loadavg)

            # Memory STATS
            memory = stat["class"].getMemStats()
            mem_percent = (memory["used"] * 100) / memory["total"]
            astmonframe.spreadsheet.SetCellValue(count,2,str(mem_percent)+"%") 
            # Check memory Threshold
            setCellThreshold(count, 2, stat["memoryth"], mem_percent)

            # Swap Stats
            swap = stat["class"].getSwapStats()
            swap_percent = (swap["used"] * 100) / swap["total"]
            astmonframe.spreadsheet.SetCellValue(count, 3,str(swap_percent)+"%")
            # Check swap stats threshold
            setCellThreshold(count, 3, stat["swapth"], swap_percent)

            # Sys Uptime
            uptime = time.gmtime(stat["class"].getSysUptime()["uptime"])
            astmonframe.spreadsheet.SetCellValue(count, 4, str(uptime[3]) + "h "
                                + str(uptime[4]) + "m " + str(uptime[5]) + "s")
            # Check sys uptime threshold
            astmonframe.spreadsheet.SetCellBackgroundColour(count, 4, GREENTH)

            # CPU USAGE
            cpu = stat["class"].getCPUPercents()
            cpu_percent = 100 - cpu["idle"]
            astmonframe.spreadsheet.SetCellValue(count, 5, 
                                          "%3.1f%%" % cpu_percent)
            # check CPU threshold
            setCellThreshold(count, 5, stat["cputh"], cpu_percent)

            # Process count
            process_count = stat["class"].getProcessCount()["total"]
            astmonframe.spreadsheet.SetCellValue(count, 6, str(process_count))
            setCellThreshold(count, 6, stat["processth"], process_count)

            # Ast uptime
            astuptime = time.gmtime(stat["class"].getAstUptime())
            astmonframe.spreadsheet.SetCellValue(count, 7, str(astuptime[3]) + "h "
                                + str(astuptime[4]) + "m " + str(astuptime[5]) + "s")
            # Check ast uptime threshold
            astmonframe.spreadsheet.SetCellBackgroundColour(count, 7, GREENTH)

            # Ast channels
            astchannels = stat["class"].getAstChannels()
            astmonframe.spreadsheet.SetCellValue(count, 8, str(astchannels))
            setCellThreshold(count, 8, stat["astchannelsth"], astchannels)

            # Ast calls
            astcalls = stat["class"].getAstCalls()
            astmonframe.spreadsheet.SetCellValue(count, 9, str(astcalls))
            setCellThreshold(count, 9, stat["astcallsth"], astcalls)

        except Pyro.errors.ProtocolError, data:
            for i in range(10):
                astmonframe.spreadsheet.SetCellBackgroundColour(count, i, REDTH)
                astmonframe.spreadsheet.SetCellValue(count, i, "")
            astmonframe.spreadsheet.Refresh()
            astmonframe.sb.SetStatusText("Connection failed: %s" % stat["name"])
            stat["class"] = Pyro.core.getProxyForURI(stat["uri"])
        finally:
            count += 1


def setCellThreshold(pbx, column, th, value):
    if value > th[1]:
        astmonframe.spreadsheet.SetCellBackgroundColour(pbx, column, REDTH)
    elif value > th[0]:
        astmonframe.spreadsheet.SetCellBackgroundColour(pbx, column, YELLOWTH)
    else:
        astmonframe.spreadsheet.SetCellBackgroundColour(pbx, column, GREENTH)

def getMin(confstr):
    return int(confstr[:confstr.find(",")].strip())

def getMax(confstr):
    return int(confstr[confstr.find(",")+1:].strip())


if __name__ == "__main__":

    astmonpyclnconf = "astmonpycln.conf"

    config = ConfigParser.ConfigParser()
    config.read(astmonpyclnconf)

    try:
        # number of clients
        numclients = config.get("clients", "num")
    except ConfigParser.NoOptionError, reason:
        print "Missing config option:", reason
        sys.exit(1)

    stats = []

    for i in range(int(numclients)):
        try:
            host = config.get("client-%d" % i, "host")
            port = config.get("client-%d" % i, "port")
            name = config.get("client-%d" % i, "name")

            try:
                diskusageth = config.get("client-%d" % i, "diskusageth")
            except ConfigParser.NoOptionError:
                diskusageth = "40, 80"

            try:
                memoryth = config.get("client-%d" % i, "memoryth")
            except ConfigParser.NoOptionError:
                memoryth = "40, 80"

            try:
                loadavgth = config.get("client-%d" % i, "loadavgth")
            except ConfigParser.NoOptionError:
                loadavgth = "1, 3"

            try:
                cputh = config.get("client-%d" % i, "cputh")
            except ConfigParser.NoOptionError:
                cputh = "20, 60"

            try:
                swapth = config.get("client-%d" % i, "swapth")
            except ConfigParser.NoOptionError:
                swapth = "20, 40"

            try:
                processth = config.get("client-%d" % i, "processth")
            except ConfigParser.NoOptionError:
                processth = "200, 300"

            try:
                astchannelsth = config.get("client-%d" % i, "astchannelsth")
            except ConfigParser.NoOptionError:
                astchannelsth = "100, 200"

            try:
                astcallsth = config.get("client-%d" % i, "astcallsth")
            except ConfigParser.NoOptionError:
                astcallsth = "50, 100"

            stats.append(   {"class" : Pyro.core.getProxyForURI(
                "PYROLOC://%s:%s/AstmonHostStats" % (host, port)),
                "name" : name, 
                "uri" : "PYROLOC://%s:%s/AstmonHostStats" % (host, port),
                "diskusageth" : [getMin(diskusageth), getMax(diskusageth)],
                "memoryth" : [getMin(memoryth), getMax(memoryth)],
                "loadavgth" : [getMin(loadavgth), getMax(loadavgth)],
                "cputh" : [getMin(cputh), getMax(cputh)],
                "swapth" : [getMin(swapth), getMax(swapth)],
                "processth" : [getMin(processth), getMax(processth)],
                "astchannelsth" : [getMin(astchannelsth),getMax(astchannelsth)],
                "astcallsth" : [getMin(astcallsth), getMax(astcallsth)] })
        except ConfigParser.NoOptionError, reason:
            print "Missing config option in client-%d, option %s" % (i, reason)
            continue
        except Pyro.errors.URIError, reason:
            print "Client %d %s" % (i, reason)
            continue


    app = wx.PySimpleApp()
    astmonframe = AstmonMainFrame(len(stats), stats)
    updateStatus(None)  
    app.MainLoop()


